﻿using System.Collections.Generic;

namespace System.Linq.Functional
{
    internal sealed class MemoizeExamples
    {
        static Func<int, int> FibonacciFast
        {
            get
            {
                var t = new Dictionary<int, int>();
                Func<int, int> fibCached = null;
                fibCached = n =>
                {
                    if (t.ContainsKey(n)) return t[n];
                    if (n <= 2) return 1;

                    var result = fibCached(n - 2) + fibCached(n - 1);
                    t.Add(n, result);
                    return result;
                };

                return n => fibCached(n);
            }
        }

        static Func<int, int> FibonacciMemoized
        {
            get
            {
                return MemoizeExtensions.Memoize((int n) => n <= 2 ? 1 : Fibonacci(n - 2) + Fibonacci(n - 1));
            }
        }

        static int Fibonacci(int n)
        {
            return n <= 2 ? 1 : Fibonacci(n - 2) + Fibonacci(n - 1);
        }

        static Table<T, U> MemoizeTable<T, U>(Func<T, U> func)
        {
            return new Table<T, U>(new Dictionary<T, U>(), func);
        }

        static Table<int, int> FibonacciTable
        {
            get
            {
                return MemoizeTable((int n) => n <= 2 ? 1 : FibonacciTable[n - 2] + FibonacciTable[n - 1]);
            }
        }

        static void Main(string[] args)
        {
            // Memoized with function
            var fibonacciFunc = FibonacciMemoized;
            var fibFast1 = fibonacciFunc(30);

            var fibFast2 = fibonacciFunc(30);
            
            // Memoize Generic
            Func<int, int> fibonacci = Fibonacci;
            var fibonacciMemoized = fibonacci.Memoize();

            var fibFast3 = fibonacciMemoized(30);
            
            var fibFast4 = fibonacciMemoized(30);
            
            // Table approach
            var fibTable = FibonacciTable;
            var fibFast5 = fibTable[30];
            
            var fibFast6 = fibTable[30];
            
            // Memoize by FibFast
            var fibFast = FibonacciFast;
            var fibFast7 = fibFast(30);
            
            var fibFast8 = fibFast(30);            
        }
    }
}
